package service

import (
	"encoding/json"
	"fmt"
	"k8s-platform/config"
	"k8s.io/client-go/tools/clientcmd"
	"log"
	"net/http"
	"time"

	"github.com/gorilla/websocket"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/tools/remotecommand"
)

// https://github.com/kubernetes/dashboard/blob/master/src/app/backend/handler/terminal.go

/*
type PtyHandler interface {
	io.Reader
	io.Writer
	remotecommand.TerminalSizeQueue
}
*/
const END_OF_TRANSMISSION = "\u0004"

// TerminalMessage is the messaging protocol between ShellController and TerminalSession.
//
// OP      DIRECTION  FIELD(S) USED  DESCRIPTION
// ---------------------------------------------------------------------
// bind    fe->be     SessionID      Id sent back from TerminalResponse
// stdin   fe->be     Data           Keystrokes/paste buffer
// resize  fe->be     Rows, Cols     New terminal size
// stdout  be->fe     Data           Output from the process
// toast   be->fe     Data           OOB message to be shown to the user
type TerminalMessage struct {
	Op, Data string
	Rows, Cols uint16 `json:",omitempty"`
}

// TerminalSession
type TerminalSession struct {
	wsConn   *websocket.Conn
	sizeChan chan remotecommand.TerminalSize
	doneChan chan struct{}
}

// TerminalSize handles pty->process resize events
// Called in a loop from remotecommand as long as the process is running
func (t *TerminalSession) Next() *remotecommand.TerminalSize {
	select {
	case size := <-t.sizeChan:
		return &size
	case <-t.doneChan:
		return nil
	}
}

// Read handles pty->process messages (stdin, resize)
// Called in a loop from remotecommand as long as the process is running
func (t *TerminalSession) Read(p []byte) (int, error) {
	_, message, err := t.wsConn.ReadMessage()
	if err != nil {
		log.Printf("read ws message failed: %v",  err)
		return copy(p, END_OF_TRANSMISSION), err
	}
	var msg TerminalMessage
	if err := json.Unmarshal(message, &msg); err != nil {
		// TODO: temp workaround for non-json input
		return 0, nil
	}
	switch msg.Op {
	case "stdin":
		return copy(p, msg.Data), nil
	case "resize":
		t.sizeChan <- remotecommand.TerminalSize{Width: msg.Cols, Height: msg.Rows}
		return 0, nil
	default:
		log.Printf(" unknown message type '%s'",  msg.Op)
		return copy(p, END_OF_TRANSMISSION), fmt.Errorf("unknown message type '%s'", msg.Op)
	}
}

// Write handles process->pty stdout
// Called from remotecommand whenever there is any output
func (t *TerminalSession) Write(p []byte) (int, error) {
	msg, err := json.Marshal(TerminalMessage{
		Op: "stdout",
		Data: string(p),
	})
	if err != nil {
		log.Printf("json parse message err: %v\n", err)
		return 0, err
	}
	if err := t.wsConn.WriteMessage(websocket.TextMessage, msg); err != nil {
		log.Printf("write ws message failed: %v",  err)
		return 0, err
	}
	return len(p), nil
}

func (t *TerminalSession) Close() error {
	close(t.doneChan)
	return t.wsConn.Close()
}
var Upgrader = func() websocket.Upgrader {
	upgrader := websocket.Upgrader{}
	upgrader.HandshakeTimeout = time.Second * 2
	upgrader.CheckOrigin = func(r *http.Request) bool {
		return true
	}
	return upgrader
}()

func newTerminalSession( r *http.Request, w http.ResponseWriter) (*TerminalSession, error) {

	conn, err := Upgrader.Upgrade(w, r, nil)
	if err != nil {
		return nil, err
	}
	return &TerminalSession{

		wsConn:   conn,
		sizeChan: make(chan remotecommand.TerminalSize),
		doneChan: make(chan struct{}),
	}, nil
}

func ContainerExec(w http.ResponseWriter, r *http.Request)  {
	//解析form入参 ，获取namespace pod container参数
	if err := r.ParseForm(); err != nil {
		log.Printf("参数解析失败" + err.Error())
		return
	}
	namespace := r.Form.Get("namespace")
	podName := r.Form.Get("pod_name")
	container := r.Form.Get("container_name")

	// 获取kube config配置
	cfg, err  := clientcmd.BuildConfigFromFlags("", config.Kubeconf)
	if err != nil {
		log.Println(err)
		return
	}

	//cmd := []string{"sh" "-c" , "test -f /bin/bash && bash || sh"} // ,
	option := &corev1.PodExecOptions{
		Command:   []string{"bash"},
		Stdin:     true,
		Stdout:    true,
		Stderr:    true,
		TTY:       true,
		Container: container,
	}
	//ctx, cancel := context.WithTimeout(ctx, models.READ_LOG_TIMEOUT)
	//defer cancel()
	req := K8s.ClientSet.CoreV1().RESTClient().
		Post().
		Resource("pods").
		Name(podName).
		Namespace(namespace).
		SubResource("exec").
		VersionedParams(option, scheme.ParameterCodec) //.Timeout(2 * time.Second)

	executor, err := remotecommand.NewSPDYExecutor(cfg, http.MethodPost, req.URL())
	if err != nil {
		log.Println(err)
		return
	}

	log.Printf("namespace:[%s] | podname:[%s] | container:[%s] \n", namespace, podName, container)
	t, err := newTerminalSession(r, w)
	if err != nil {
		log.Println(err)
		return
	}
	defer func() {
		log.Println("关闭Terminal")
		t.Close()
	}()

	err = executor.Stream(remotecommand.StreamOptions{
		Stdin:             t,
		Stdout:            t,
		Stderr:            t,
		TerminalSizeQueue: t,
		Tty:               true,
	})

	if err != nil {
		// http连接已经被hijacked，http.ResponseWriter不能再使用，所以不返回err
		t.Write([]byte("执行pod命令失败：" + err.Error()))
		log.Printf("exec stream failed: %v", err)
		return
	}


}

